home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 2 / Gold Medal Software Volume 2 (Gold Medal) (1994).iso / prog / asm_n_z.arj / SPY'.ASM < prev    next >
Assembly Source File  |  1987-05-11  |  17KB  |  430 lines

  1. name spyprime
  2.  
  3. rt        equ    0dh    ; return
  4. lf        equ    0ah    ; linefeed
  5. eof        equ    1ah    ; end of file
  6. ok_seg        equ    1    ; flag for segment decode check
  7. ok_off        equ    2    ; flag for offset decode check
  8. max_size    equ    4095    ; maximum size for SPY' search region
  9. beep_delay    equ    4    ; number of clock ticks between beeps
  10. timer_count    equ    597    ; frequency of beep = 1,193,180 / timer_count
  11.  
  12. ; These constants are used by the newint16h routine in this, the SPY' program.
  13. ; They are not needed for the SPY program.
  14. ckey1        equ    20cdh    ; value of first key (a "backwords" int 20)
  15. ckey2        equ    1234h    ; value of second key
  16. ckey3        equ    5678h    ; value of third key
  17.  
  18. code        segment
  19.         assume cs:code, ds:code
  20.  
  21.         org    0
  22. key1        dw    ?    ; offset of int 20h instruction
  23.                 ;   used to verify a PSP location
  24.  
  25.         org    100h
  26.  
  27. begin:        jmp    start    ; jump to program start location
  28.  
  29. ;-------------------------------------------------------------------------------
  30. ; This copyright message appears when SPY'.COM is "typed" to the screen.
  31. ;-------------------------------------------------------------------------------
  32.  
  33.         db    8,8,8,'   '
  34. copyright    db    rt,lf
  35.         db    "SPY' Copyright (C) 1987 by Charles Lazo III, v1.0"
  36.         db    eof,8,' ',rt,lf,'$'
  37.  
  38. key2        dw    ckey2    ; keys used to determine prior load of SPY'
  39. key3        dw    ckey3
  40.  
  41. oldint8             dd    ?    ; double word storage for old int 8 vector
  42. oldint16h    dd    ?    ; double word storage for old int 16 vector
  43. beeps        dw    0    ; number of beeps (notices) to send to user
  44. delay        dw    1    ; number of clock ticks 'til next beep
  45. beep_on        db    0    ; beep on/off status
  46. spy_size    dw    ?    ; number of bytes to spy on
  47. resident    db    0    ; indicates prior load of SPY' (0 = none)
  48.  
  49. spy_location    label    dword
  50. spy_off        dw    ?    ; storage for value of offset for SPYing
  51. spy_seg        dw    ?    ; storage for value of segment for SPYing
  52.  
  53. ;-------------------------------------------------------------------------------
  54. ; Comparisons of the spied region and the spy buffer (set up initially as a copy
  55. ; of the spied region) are made in this addition to the clock interrupt.
  56. ; Changes to the spied region are counted and the spy buffer is updated.  The
  57. ; speaker is turned on and off here, but frequency is set during program
  58. ; initialization.
  59. ;-------------------------------------------------------------------------------
  60.  
  61. newint8        proc    near
  62.         push    ax        ; save interrupted program's registers
  63.         push    bx
  64.         push    cx
  65.         push    si
  66.         push    di
  67.         push    ds
  68.         push    es
  69.  
  70.         mov    ax,cs        ; our data lies here
  71.         mov    ds,ax
  72.  
  73.         lea    si,spy_buffer    ; point to our buffer
  74.         les    di,spy_location    ; point to spied region
  75.         mov    cx,spy_size    ; compare whole region
  76.         cld            ; forward!
  77. cmp_more:    repz    cmpsb        ; compare until no match or cx = 0
  78.         jz    cmp_done    ; if zero, then cx = 0 and we're done
  79.         inc    beeps        ; account for a change
  80.         mov    al,es:[di-1]    ; change accounted; update spy_buffer
  81.         mov    [si-1],al
  82.         or    cx,cx        ; set zero flag by cx (avoid inf loop)
  83.         jmp    short cmp_more    ; continue 'til done
  84.  
  85. cmp_done:    cmp    beep_on,0    ; is the beep on?
  86.         jz    do_beep?    ; no, shall we do a beep?
  87.         dec    beep_on        ; yes, turn it off
  88.         in    al,97        ; get speaker control bits
  89.         and    al,0fch        ; set them off
  90.         out    97,al        ; turn off speaker
  91.         jmp    short exit    ; job done; get out
  92. do_beep?:    cmp    beeps,0        ; are there beeps to be done?
  93.         jz    delay1        ; no, get out
  94.         dec    delay        ; reduce delay 'til next beep
  95.         jnz    exit        ; not zero, then exit
  96.         in    al,97        ; get speaker control bits
  97.         or    al,3        ; set them on
  98.         out    97,al        ; turn on speaker
  99.         inc    beep_on        ; signal beep is on
  100.         mov    delay,beep_delay; reinitialize delay counter
  101.         dec    beeps        ; one less beep to do
  102.         jmp    short exit    ; leave now
  103. delay1:        mov    delay,1        ; don't wait for first beep of a series
  104. exit:        pop    es        ; restore registers
  105.         pop    ds
  106.         pop    di
  107.         pop    si
  108.         pop    cx
  109.         pop    bx
  110.         pop    ax
  111.         jmp    dword ptr cs:oldint8    ; continue with interrupt
  112. newint8        endp
  113.  
  114. ;-------------------------------------------------------------------------------
  115. ; Keyboard interrupt, int 16h, is issued with function number ah = 77h in the
  116. ; set_es routine.  If there has been no prior load of SPY', then the interrupt
  117. ; returns without changing anything.  However, if SPY' has been run previously,
  118. ; then this routine has been chained into the interrupt 16h handler and the
  119. ; issue of "keyboard" function number 77h causes the returned value of es to be
  120. ; what cs was for the original load of SPY'.
  121. ;-------------------------------------------------------------------------------
  122.  
  123. newint16h    proc    near
  124.         cmp    ah,77h        ; our cue?
  125.         jne    not_us        ; nah, not us
  126.         push    ax        ; save this
  127.         mov    ax,ckey1    ; have we a match for the first key?
  128.         cmp    ax,cs:key1
  129.         je    test_2nd    ; yes, test the 2nd key
  130.         jmp    short onward    ; no previous load found
  131. test_2nd:    mov    ax,ckey2    ; have we a match for the second key?
  132.         cmp    ax,cs:key2
  133.         je    test_3rd    ; yes, test the 3rd key
  134.         jmp    short onward    ; no previous load found
  135. test_3rd:    mov    ax,ckey3    ; have we a match for the third key?
  136.         cmp    ax,cs:key3
  137.         jne    onward        ; no match => no previous load
  138.         pop    ax        ; unstack saved value
  139.         mov    ax,cs        ; pass this cs in es
  140.         mov    es,ax
  141.         mov    ax,ckey1    ; pass these to inform set_es
  142.         mov    bx,ckey2
  143.         iret
  144. onward:        pop    ax        ; recover this
  145. not_us:        jmp    dword ptr cs:oldint16h    ; continue with interrupt
  146. newint16h    endp
  147.  
  148. ;-------------------------------------------------------------------------------
  149. ; Here the region spied upon is copied to an area of the TSR that remains in
  150. ; memory after termination, the spy_buffer.  The es register is equal either to
  151. ; cs if SPY' has not yet been loaded or to the value of cs when SPY' was loaded
  152. ; previously (accomplished by a call to the set_es routine).
  153. ;-------------------------------------------------------------------------------
  154.  
  155. copy_spied:    lea    di,spy_buffer    ; destination of copy
  156.         mov    si,spy_off    ; the offset is source of copy
  157.         mov    cx,spy_size    ; number of bytes to copy
  158.         mov    ax,spy_seg    ; load segment of SPY' region to ds
  159.         push    ds        ; SOD (save our data)
  160.         mov    ds,ax
  161.         cld            ; forward copy
  162.         rep    movsb        ; copy the SPY' region to spy_buffer
  163.         pop    ds        ; RestoreOD
  164.  
  165.         cmp    resident,0    ; is SPY' currently resident in memory?
  166.         je    tsr        ; no, make it so
  167.         mov    ax,4c00h    ; yes, end with error code 0
  168.         int    21h
  169.  
  170. ;-------------------------------------------------------------------------------
  171. ; SPY' has not yet been loaded into memory, so we do it now.  First the 8253-5
  172. ; chip that generates the frequency of the beeps is initialized with the value
  173. ; given by the constant timer_count.  Then the new interrupt 8 routine is
  174. ; spliced in and finally the constant max_size is used to reserve enough memory
  175. ; for the largest permissible spy buffer.
  176. ;-------------------------------------------------------------------------------
  177.  
  178. tsr:        mov    delay,beep_delay; initialize delay counter
  179.  
  180.         ; set 8253-5 programmable timer to chosen frequency
  181.         mov    al,182        ; byte to initialize 8253 timer
  182.         out    67,al        ; tell timer next two bytes are count
  183.         mov    ax,timer_count    ; get timer count
  184.         out    66,al        ; output low byte
  185.         mov    al,ah
  186.         out    66,al        ; output high byte
  187.  
  188.         mov    ax,3508h    ; get the interrupt 8 (clock) vector
  189.         int    21h        ;   with DOS function 35h call
  190.  
  191. ; Retain offset and segment of interrupt 8 vector:
  192.  
  193.         mov    word ptr cs:oldint8,bx
  194.         mov    word ptr cs:oldint8+2,es
  195.  
  196.         lea    dx,newint8    ; place offset in dx
  197.         mov    ax,2508h    ; set its pointer into interrupt table
  198.         int    21h        ;   with DOS function 25h call
  199.  
  200.         mov    ax,3516h    ; get the int 16h (keyboard) vector
  201.         int    21h        ;   with DOS function 35h call
  202.  
  203. ; Retain offset and segment of interrupt 16h vector:
  204.  
  205.         mov    word ptr cs:oldint16h,bx
  206.         mov    word ptr cs:oldint16h+2,es
  207.  
  208.         lea    dx,newint16h    ; place offset in dx
  209.         mov    ax,2516h    ; set its pointer into interrupt table
  210.         int    21h        ;   with DOS function 25h call
  211.  
  212.         lea    dx,spy_buffer    ; where SPY' buffer begins
  213.         mov    cx,max_size    ; add the maximum size of the buffer:
  214.         add    dx,cx
  215.         mov    cl,4        ; compute number of paragraphs to save:
  216.         shr    dx,cl        ;   bytes to paragraphs in dx
  217.         inc    dx        ;   insure sufficient size for buffer
  218.         mov    ax,3100h    ; terminate but stay resident code = 0
  219.         int    21h
  220.  
  221. ;-------------------------------------------------------------------------------
  222. ; The following is initialization code and data that is overwritten by the data
  223. ; copied to the spy buffer when this buffer is initialized with a copy of the
  224. ; spied region.
  225. ;-------------------------------------------------------------------------------
  226.  
  227. spy_buffer:    ; this is where SPY' will store a copy of the SPY' region
  228.         ;   to later check for any changes to the region
  229.  
  230. syntax        db    rt,lf,"SPY' syntax:  SPY' xxxx:yyyy L zzz",rt,lf,rt,lf
  231.         db    'Where xxxx, and yyyy are hexadecimal numbers up to '
  232.         db    'four digits in length and',rt,lf
  233.         db    "zzz is a one to three digit hexadecimal number.  SPY' "
  234.         db    'will monitor the segment-',rt,lf
  235.         db    'offset region xxxx:yyyy for a length of zzz bytes and '
  236.         db    'report to the user with a',rt,lf
  237.         db    'number of beeps equal to the number of bytes changed '
  238.         db    'in that region if and when',rt,lf
  239.         db    'any are changed.',rt,lf,'$'
  240.  
  241. progress    db    0    ; flags for progress of command line conversion
  242.  
  243. ;-------------------------------------------------------------------------------
  244. ; This routine will convert an ASCII hex digit in al (with either upper or lower
  245. ; case alpha hex) into a 1-of-16 binary value in al.
  246. ;-------------------------------------------------------------------------------
  247.  
  248. make_binary    proc    near        ; hex ASCII digit in al to binary in al
  249.         cmp    al,'0'        ; less than "0", then not hex digit
  250.         jb    not_digit
  251.         cmp    al,'f'        ; greater than "f", then not hex digit
  252.         ja    not_digit
  253.         cmp    al,'9'        ; test for numeric digit
  254.         ja    not_numeric    ; not a numeric digit
  255.         sub    al,'0'        ; convert to binary
  256.         ret
  257. not_numeric:    cmp    al,'F'        ; over "F"?
  258.         ja    low_case    ; yes, test for lower case
  259.         cmp    al,'A'        ; less than "A"?
  260.         jb    not_digit    ; yes, not hex digit
  261.         sub    al,37h        ; convert to binary
  262.         ret
  263. low_case:    cmp    al,'a'        ; less than "a"?
  264.         jb    not_digit    ; yes, not hex digit
  265.         sub    al,57h        ; convert to binary
  266.         ret
  267. not_digit:    stc            ; set carry flag if not hex digit
  268.         ret
  269. make_binary    endp
  270.  
  271. ;-------------------------------------------------------------------------------
  272. ; This routine is called to allow the command line parse routines to ignore any
  273. ; space and tab characters on the command line that do not lie in hex numbers.
  274. ; If the return character at the end of the command line is encountered, then
  275. ; the carry flag is set; otherwise it is reset.
  276. ;-------------------------------------------------------------------------------
  277.  
  278. skip        proc    near        ; skip over spaces and tabs; signal
  279.                     ;   a RETURN with the carry flag
  280. more_skip:    cmp    al,' '        ; is it a space?
  281.         je    another        ; if so, get another
  282.         cmp    al,9        ; is it a tab?
  283.         je    another        ; if so, get another
  284.         cmp    al,rt        ; is it a RETURN?
  285.         je    a_rt        ; yes, set carry and return
  286.         clc            ; not one of these three so return
  287.         ret            ;   with carry clear
  288. another:    lodsb            ; get another character
  289.         jmp    short more_skip    ; and try again
  290. a_rt:        stc            ; return with carry set
  291.         ret
  292. skip        endp
  293.  
  294. ;-------------------------------------------------------------------------------
  295. ; Here we parse the command line, which by the conditions of proper syntax has
  296. ; the three hexadecimal values for segment, offset and byte size for the region
  297. ; which is to be spied.  The routine between  cnv_more:  and  cnv_error:  is run
  298. ; once each to decode segment, offset and size which are stored respectively in
  299. ; the variables spy_seg, spy_off, and spy_size unless a syntax error is found.
  300. ; The variable progress is used to keep track of which of these three numbers
  301. ; has just been decoded.  A semicolon can be used to preface comments at the end
  302. ; of the command line.
  303. ;-------------------------------------------------------------------------------
  304.  
  305. convert        proc    near        ; convert hex ASCII to binary in ax
  306.         mov    cx,16        ; for hex to binary conversion
  307. cnv_more:    xor    bx,bx        ; accumulate result here
  308.         xor    ah,ah        ; need this zero too
  309.         dec    si        ; move back to last character
  310. cycle:        lodsb            ; get possible ASCII hex digit
  311.         call    make_binary    ; if ASCII hex, then make binary in al
  312.         jc    test_seg    ; if carry set, then char not hex digit
  313.         xchg    ax,bx        ; accumulation in ax, last digit in bx
  314.         mul    cx        ; bump to next power
  315.         or    dx,dx        ; test for overflow
  316.         jnz    cnv_error    ; result can't be larger than fff0h
  317.         add    ax,bx        ; add in most recent digit
  318.         xchg    ax,bx        ; accumulation in bx, ah = 0
  319.         jmp    short cycle    ; continue conversion
  320. cnv_error:    pop    ax        ; remove return location (of call)
  321.         jmp    stax        ; display syntax and end
  322. test_seg:    test    progress,ok_seg    ; have we decoded the segment value?
  323.         jnz    test_off    ; yes, test offset
  324.         call    skip        ; skip over spaces and tabs
  325.         jc    cnv_error    ; carry flag set if RETURN was found
  326.         cmp    al,':'        ; syntax says must have colon here
  327.         jne    cnv_error
  328.         lodsb            ; get another
  329.         call    skip        ; skip over spaces and tabs
  330.         jc    cnv_error    ; carry flag set if RETURN was found
  331.         mov    spy_seg,bx    ; store value of segment for SPYing
  332.         or    progress,ok_seg    ; segment value is now decoded
  333.         jmp    short cnv_more    ; continue to convert offset and size
  334. test_off:    test    progress,ok_off    ; have we decoded the offset value?
  335.         jnz    test_size    ; yes, test size
  336.         call    skip        ; skip over spaces and tabs
  337.         jc    cnv_error    ; carry flag set if RETURN was found
  338.         and    al,0dfh        ; convert "L" or "l" to "L"
  339.         cmp    al,'L'        ; syntax says must have "l" or "L" here
  340.         jne    cnv_error
  341.         lodsb            ; get another
  342.         call    skip        ; skip over spaces and tabs
  343.         jc    cnv_error    ; carry flag set if return was found
  344.         mov    spy_off,bx    ; store value of offset for SPYing
  345.         or    progress,ok_off    ; offset value is now decoded
  346.         jmp    short cnv_more    ; continue to convert size
  347. test_size:    cmp    bx,max_size    ; size must not be over maximum
  348.         ja    cnv_error
  349.         call    skip        ; skip over spaces and tabs
  350.         jnc    comment        ; shall allow comments at cmd line end
  351. cnv_finished:    mov    spy_size,bx    ; store away size
  352.         ret
  353. comment:    cmp    al,';'        ; test for comment symbol
  354.         jne    cnv_error    ; not comment symbol, then error
  355.         jmp    short cnv_finished    ; all done
  356. convert        endp
  357.  
  358. ;-------------------------------------------------------------------------------
  359. ; This is the routine that contains the code that this program was designed to
  360. ; demonstrate.  It serves to detect if SPY' has been loaded previously as a TSR.
  361. ; If it has, then the code segment at the time of the previous load will be put
  362. ; into es; if not, then es will return with the current value of cs.  The method
  363. ; used attempts to find the set of key values key1, key2, and key3 in the memory
  364. ; above the current PSP.  key1 is word cd20h which is the int 20h instruction
  365. ; found at offset 0 of every PSP.  If key1 matches, then key2 is tried and if it
  366. ; matches key3 is tried.  A triple match gives fair confidence that SPY' has
  367. ; been run as a TSR once before.  It is even possible to gain further confidence
  368. ; or detect the presence of a different version of the same program by comparing
  369. ; a string in the returned es to a string in cs, say the copyright notice.  Note
  370. ; that the keys are compared in the newint16h routine that handles the function
  371. ; 77h if SPY' has been previously loaded.
  372. ;-------------------------------------------------------------------------------
  373.  
  374. set_es        proc    near        ; es becomes cs of original SPY' load
  375.         mov    ah,77h        ; function to find previous load address
  376.         int    16h
  377.         cmp    ax,ckey1    ; if ax = ckey1
  378.         jne    not_loaded
  379.         cmp    bx,ckey2    ; and bx = ckey2
  380.         jne    not_loaded
  381.         mov    resident,1    ; then there's been a prior load
  382.         ret
  383. not_loaded:    mov    ax,cs        ; else, return with es = cs
  384.         mov    es,ax
  385.         ret
  386. set_es        endp
  387.  
  388. ;-------------------------------------------------------------------------------
  389. ; Execution starts here.  The command line is decoded, new parameters are passed
  390. ; to the prior load if it exists, and the beeps variable is set to zero for a
  391. ; fresh start.  Execution is then passed to  copy_spied:  and other termination
  392. ; code that can not be overwritten by the copy process.
  393. ;-------------------------------------------------------------------------------
  394.  
  395. start:        lea    dx,copyright    ; display copyright
  396.         mov    ah,9
  397.         int    21h
  398.  
  399.         mov    bx,80h        ; get command line byte count
  400.         mov    al,[bx]
  401.         cmp    al,1
  402.         ja    parse        ; if command given, parse it
  403. stax:        lea    dx,syntax    ; display command syntax
  404.         mov    ah,9
  405.         int    21h
  406.         mov    ax,4c01h    ; and exit with error code 1
  407.         int    21h
  408.  
  409. parse:        mov    si,82h        ; address first usable character
  410.         lodsb
  411.         call    skip        ; skip over spaces and tabs
  412.         jc    stax        ; carry is set if RETURN was found
  413.         call    convert        ; convert and store hex values
  414.         call    set_es        ; set es to spy_buffer segment
  415.  
  416.         mov    ax,spy_seg    ; set SPY' region to newly given values
  417.         mov    es:spy_seg,ax    ;   (this isn't necessary for first
  418.         mov    ax,spy_off    ;    time through, but it doesn't
  419.         mov    es:spy_off,ax    ;    hurt either.)
  420.         mov    ax,spy_size
  421.         mov    es:spy_size,ax
  422.  
  423.         xor    ax,ax        ; initialize beeps count
  424.         mov    es:beeps,ax
  425.  
  426.         jmp    copy_spied    ; copy that which is to be spied
  427.  
  428. code        ends
  429.         end    begin
  430.